﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel.Composition;
using System.IO;
using IndicoInterface;
using System.Diagnostics;
using DeepTalkRenderServiceLib;
using System.Threading.Tasks;

namespace DeepTalkRenderShell.App.TalkRendering
{
    /// <summary>
    /// We run the DT render app!
    /// </summary>
    [Export(typeof(IRenderTalk))]
    [ExportMetadata("Name", "RenderRunDTRenderClientApp")]
    class RenderRunDTRenderClientApp : IRenderTalk
    {
        public DeepTalkRenderServiceLib.RenderMeetingTask.RenderResultsInfo RenderTalk(string agendaXML, int talkID)
        {
            Trace.WriteLine("Called to render talk id=" + talkID);

            ///
            /// Get the URL we are going to process
            /// 

            var ainfo = AgendaInfo.Deseralize(agendaXML);
            if (ainfo == null)
            {
                throw new ArgumentException("The AgendaXML could not be parsed into a real talk for talk ID " + talkID);
            }

            ///
            /// Get the output directory we will be useing to write everything
            /// 

            string outputDirFormatString = Settings.Default["DTOutputDirectoryFormatString"] as string;
            if (outputDirFormatString == null)
            {
                throw new ArgumentException("DTOutputDirectoryFormatString not set in the settings file!");
            }
            StringBuilder bldOutputDir = new StringBuilder();
            bldOutputDir.AppendFormat(outputDirFormatString, talkID);

            DirectoryInfo outputDir = new DirectoryInfo(bldOutputDir.ToString());
            if (!outputDir.Exists)
            {
                outputDir.Create();
            }

            ///
            /// Write out a file that contains the xml - this is what the program will use as input...
            /// 

            FileInfo xmlFile = new FileInfo(outputDir.FullName + "\\agenda.xml");
            using (var xmlWriter = xmlFile.CreateText())
            {
                xmlWriter.WriteLine(agendaXML);
                xmlWriter.Close();
            }

            ///
            /// Open a log file which we will dump out there!
            /// 

            FileInfo outputLogPath = new FileInfo(outputDir.FullName + "\\RenderingShellLog.txt");
            using (var logger = new StreamWriter(outputLogPath.Open(FileMode.Append, FileAccess.Write)))
            {
                logger.AutoFlush = true;
                logger.WriteLine("{0} Starting agenda from site {1} subdur {2} and confID {3}", DateTime.Now, ainfo.AgendaSite, ainfo.AgendaSubDirectory, ainfo.ConferenceID);

                ///
                /// Start the process now, and then just listen in for the items that come out of it.
                /// 

                ProcessStartInfo pi = new ProcessStartInfo(Settings.Default.DTProgramLocation);
                pi.Arguments = Quote(xmlFile.FullName) + " " + Quote(outputDir.FullName);
                pi.CreateNoWindow = false;
                pi.ErrorDialog = false;
                pi.RedirectStandardError = true;
                pi.RedirectStandardOutput = true;
                pi.RedirectStandardInput = true;
                pi.UseShellExecute = false;
                pi.WorkingDirectory = Settings.Default.DTProgramWorkingDirectory;

                ///
                /// Start the process
                /// 

                Trace.WriteLine("Starting up remote process " + pi.FileName);
                var p = Process.Start(pi);
                p.StandardInput.Close();

                ///
                /// Now we have to watch things until it finishes...
                /// 

                var tout = Task.Factory.StartNew(() => DumpSTDOut(logger, p.StandardOutput));
                var terr = Task.Factory.StartNew(() => DumpSTDOut(logger, p.StandardError));

                tout.Wait();
                terr.Wait();
                p.WaitForExit();
                logger.WriteLine("{0} - Render done! Bye!", DateTime.Now);

                Trace.WriteLine("Finished running the external process");

                ///
                /// Done. Clean up now!
                /// 

                ///
                /// Now, look for the output file, and we will use that to extract the info we need from this thing.
                /// 

                FileInfo jobMetaData = new FileInfo(outputDir.FullName + "\\renderJobMetaData.xml");
                if (!jobMetaData.Exists)
                {
                    Trace.WriteLine("The render job did not leve behind a render file");
                    logger.WriteLine("{0} The render job did not leave behind a render xml file!", DateTime.Now);

                    var dummy = new DeepTalkRenderServiceLib.RenderMeetingTask.RenderResultsInfo()
                    {
                         HowLongToNextUpdate = new TimeSpan(1, 0, 0),
                         NumberOfTalks = 0,
                         Title = "Render Failed",
                         MeetingEnd = new DateTime(1980, 1, 2),
                         MeetingStart = new DateTime(1980, 1, 1),
                         SizeOfRender = (float)0.0

                    };
                    return dummy;
                }
                var result = jobMetaData.ConvertToRenderResults();

                Trace.WriteLine("# of talks rendered: " + result.NumberOfTalks);
                Trace.WriteLine("How long till next update: " + (result.HowLongToNextUpdate.HasValue ? result.HowLongToNextUpdate.Value.ToString() : "none requested"));

                ///
                /// Done!
                /// 

                logger.WriteLine("{0} Time To Next Update: {1}", DateTime.Now, result.HowLongToNextUpdate.HasValue ? result.HowLongToNextUpdate.Value.ToString() : "No Further Updates Requested");

                logger.Close();
                return result;
            }

        }

        /// <summary>
        /// Helper to dump everythign until EOF.
        /// </summary>
        /// <param name="logger"></param>
        /// <param name="input"></param>
        private void DumpSTDOut(StreamWriter logger, StreamReader input)
        {
            while (!input.EndOfStream)
            {
                string line = input.ReadLine();
                logger.WriteLine("{0} {1}", DateTime.Now, line);
            }

        }

        /// <summary>
        /// Helper function - surrounds a string with quotes
        /// </summary>
        /// <param name="p"></param>
        /// <returns></returns>
        private string Quote(string str)
        {
            return "\"" + str + "\"";
        }
    }
}
